﻿using System;
using System.Collections.Generic;
using System.Collections;

namespace Simp.CodeModel.CoreFunctions.Sequences
{
	/// <summary>Encapsulates a function cumulating application of a function over a sequence.</summary>
	/// <remarks>
	/// <para>
	/// For instance, <c>Cumulate({a, b, c}, f)</c> will yield <c>{a, f(a,b), f(f(a,b), c)}</c>.
	/// </para>
	/// <para>
	/// A typical application is with the <c>Addition</c> function where the cumulate function yields the corresponding serie.
	/// </para>
	/// </remarks>
	public class CumulateFunction : IFunction, ISequenceFunction
	{
		#region Inner Types
		private class CumulateSequenceAccessor : ISequenceAccessor
		{
			private readonly ISequenceAccessor _sourceAccessor;
			private readonly FunctionReference _f;

			public CumulateSequenceAccessor(ISequenceAccessor sourceAccessor, FunctionReference f)
			{
				_sourceAccessor = sourceAccessor;
				_f = f;
			}

			#region ISequenceAccessor Members
			int ISequenceAccessor.Count
			{
				get { return _sourceAccessor.Count; }
			}

			IFunction ISequenceAccessor.this[int index]
			{
				get
				{
					foreach (IFunction cumulatedElement in this)
					{
						if (index == 0)
						{
							return cumulatedElement;
						}
						--index;
					}

					throw new NotSupportedException("This point should never be reached");
				}
			}
			#endregion

			#region IEnumerable<IFunction> Members
			IEnumerator<IFunction> IEnumerable<IFunction>.GetEnumerator()
			{
				IFunction cumulator = null;

				foreach (IFunction sourceElement in _sourceAccessor)
				{
					if (cumulator == null)
					{
						cumulator = sourceElement;
					}
					else
					{
						cumulator = new BoundFunctionCalling(_f.InnerFunction, cumulator, sourceElement);
					}

					yield return cumulator;
				}
			}
			#endregion

			#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<IFunction>)this).GetEnumerator();
			}
			#endregion
		}
		#endregion

		private static readonly string[] PARAMETER_NAMES = new[] { "source", "f" };

		#region IFunction Members
		string IFunction.FunctionName
		{
			get { return "Cumulate"; }
		}

		string[] IFunction.ParameterNames
		{
			get { return PARAMETER_NAMES; }
		}

		IFunctionParameterBinding[] IFunction.DefaultParameterValues
		{
			get { return FunctionHelper.EmptyBindings; }
		}

		IFunction IFunction.Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			return null;
		}

		IFunction IFunction.AcceptVisitor(IFunctionVisitor visitor)
		{
			return null;
		}
		#endregion

		#region ISequenceFunction Members
		ISequenceAccessor ISequenceFunction.GetAccessor(params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			ISequenceAccessor accessor = FunctionHelper.GetAccessor(parameters[0]);
			FunctionReference f = parameters[1] as FunctionReference;

			if (accessor == null || f == null)
			{
				return null;
			}
			else
			{
				return new CumulateSequenceAccessor(accessor, f);
			}
		}
		#endregion
	}
}